//
// Created by bill3000 on 2/16/21.
// 使用方法如下：
// 第一步，调用初始化函数Logger::start_logger_thread("/intelli/logger/log.txt")创建日志文件;
// 第二步，调用LOG(INFO/WARNING/ERROR, ”your info”),your info表示你要输入到日志文件中的信息。
// 第3步, after finishing, call Logger::dispose() to release resources
//

#include <sys/msg.h>
#include <sys/ipc.h>
#include <ctime>
#include <cstring>

#include "logger.hpp"

extern bool running;
pthread_t Logger::logger_thread_id;  //public static member

int Logger::m_mq_id;  //private static member
std::ofstream *Logger::m_log_file = nullptr;  //private static member

void Logger::start_logger_thread(const std::string& file_name) {
    m_log_file = new std::ofstream();
    m_log_file->open(file_name.c_str(), std::ios::out | std::ios::app);
    if (!m_log_file->is_open()) {
        std::cout << "Can not open logger file !";
        exit(1);
    }

    key_t key;
    key = ftok("/intelli/message/mlog",2266);
    m_mq_id = msgget(key, IPC_CREAT | 0666);
    if (m_mq_id <= -1) {
        std::cout << "Cannot create message queue for logger!";
        exit(1);
    }
    start_logger_thread();
}

void Logger::wait_logger_thread() {
    pthread_join(logger_thread_id, nullptr);
}

void Logger::stop_logger_thread() {
    pthread_cancel(logger_thread_id);
}

void Logger::dispose() {
    if (m_log_file->is_open()) {
        m_log_file->close();
        delete m_log_file;
        m_log_file = nullptr;
    }
    msgctl(m_mq_id, IPC_RMID, nullptr);
}

void Logger::start_logger_thread() {
    int ret;
    ret = pthread_create(&logger_thread_id, nullptr, logger_thread, (void *) nullptr);
    if(ret!=0) {
        std::cout << "Failed to create logger thread !";
        exit(1);
    }
}

void* Logger::logger_thread(void *) {
    LogMessage lm;
    while(running) {
        memset((void *)&lm, 0, sizeof(lm));
        if (msgrcv(m_mq_id, (void *)&lm, sizeof(lm.text), 0, MSG_NOERROR) < 0) {
            std::cout << strerror(errno);
            continue;
        }
        if (m_log_file->tellp() <= 5000000)  //make sure not too large log file
            *m_log_file << lm.text << std::flush;
    }
    pthread_exit(nullptr);
}

void Logger::start(LogLevel level, const std::string& message,
                   const std::string& file, int line, const std::string& function) {
    std::string buf;

    std::string s1;
    if (level == INFO) s1 = "Info: ";
    else if (level == WARNING) s1 = "Warning: ";
    else if (level == ERROR) s1 = "Error: ";
    else s1 = "Fatal: ";
    buf.append(s1).append("\n");

    time_t tm;
    time(&tm);
    char ts[128];
    ctime_r(&tm, ts);
    buf.append(ts);

    std::string s2 = file.substr(file.find_last_of('/'));
    buf.append("File: ").append(s2).append(". ");
    sprintf(ts, "%d. ", line);
    buf.append("Function: ").append(function).append(". ")
                            .append("Line: ").append(ts).append("\n");

    buf.append(message).append("\n\n");

//    Debug used
    std::cout << message << std::endl;

    LogMessage lm;
    memset((void *)&lm, 0, sizeof(lm));
    lm.mtype = 1;
    strcpy(lm.text, buf.c_str());
    if(msgsnd(m_mq_id,(void *)&lm, buf.size(),0) < 0) {
        std::cout << strerror(errno);
    }
}
